package com.example.lemonservice.module.login.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.lemoncommon.auth.AuthConfig;
import com.example.lemoncommon.auth.AuthConstant;
import com.example.lemoncommon.entity.UserTokenEntity;
import com.example.lemoncommon.enums.ErrorEnum;
import com.example.lemoncommon.enums.UserErrorEnum;
import com.example.lemoncommon.exception.ServiceException;
import com.example.lemoncommon.util.JsonUtils;
import com.example.lemoncommon.util.Md5Util;
import com.example.lemoncommon.util.RedisUtil;
import com.example.lemoncommon.util.UserTokenUtils;
import com.example.lemonservice.entitiy.User;
import com.example.lemonservice.entitiy.dto.LoginDTO;
import com.example.lemonservice.entitiy.vo.LoginUserVO;
import com.example.lemonservice.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.lang.JoseException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class LoginServiceImpl implements ILoginService {

    public static final String LOGIN_TIMES = "loginTimes:";

    private static final int TOKEN_EXPIRED_DAYS = 30;
    @Resource
    private RedisUtil redisUtil;


    @Resource
    private UserMapper userMapper;


    @Value("${login.times.limit}")
    private int loginTimesLimit;

    @Value("${login.times.expire}")
    private int loginTimesExpire;

    @Resource
    private AuthConfig authConfig;

    @Override
    public LoginUserVO login(LoginDTO request) {
        User user = getUser(request);
        return createToken(user);
    }

    @Override
    public boolean logOut(final String token) {
        try {
            UserTokenEntity userTokenEntity = UserTokenUtils.parseToken(token);
            redisUtil.delete(AuthConstant.LOGIN_TOKEN_PLATFORM_KEY + userTokenEntity.getUserName());
        } catch (JoseException e) {
            throw new ServiceException(ErrorEnum.DECRYPT_USER_TOKEN_EXCEPTION.getErrCode(), ErrorEnum.DECRYPT_USER_TOKEN_EXCEPTION.getErrMsg());
        } catch (InvalidJwtException e) {
            throw new ServiceException(ErrorEnum.DECRYPT_USER_TOKEN_EXCEPTION.getErrCode(), ErrorEnum.DECRYPT_USER_TOKEN_EXCEPTION.getErrMsg());
        } catch (Exception e) {
            throw new ServiceException("退出登录失败！");
        }
        return true;
    }

    private LoginUserVO createToken(final User model) {
        final String token = UserTokenUtils.createToken(model.getId() + "", model.getUserName());
        if (token == null) {
            throw new ServiceException(ErrorEnum.CREATE_USER_TOKEN_EXCEPTION.getErrCode(), ErrorEnum.CREATE_USER_TOKEN_EXCEPTION.getErrMsg());
        }
        final boolean saveRedisSuccess = redisUtil.set(AuthConstant.LOGIN_TOKEN_PLATFORM_KEY + model.getUserName(), token, authConfig.getTokenExpireTime());
        if (!saveRedisSuccess) {
            throw new ServiceException(ErrorEnum.SAVE_TOKRN_FAIL.getErrCode(), ErrorEnum.SAVE_TOKRN_FAIL.getErrMsg());
        }
        final LoginUserVO vo = new LoginUserVO();
        vo.setId(model.getId());
        vo.setUserName(model.getUserName());
        vo.setToken(token);

        /* 保存用户信息至redis */
        final String string = JsonUtils.pojoToJson(model);
        final boolean saveUserInfo = redisUtil.set(AuthConstant.PLATFORM_USER_INFO_KEY + model.getUserName(), string, authConfig.getTokenExpireTime());
        if (!saveUserInfo) {
            throw new ServiceException(ErrorEnum.SAVE_USER_FAIL.getErrCode(), ErrorEnum.SAVE_USER_FAIL.getErrMsg());
        }

        /* 用户权限至redis */
        return vo;
    }

    private User getUser(LoginDTO request) {
        String name = request.getName();
        String password = request.getPassword();
        if (StringUtils.hasText(name)) {
            name = name.trim();
        }
        if (!StringUtils.hasText(name)) {
            throw new ServiceException(ErrorEnum.BAS_FAIL.getErrCode(), "用户名不能为空");
        }
        User user = new User();
        user.setUserName(name);
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.setEntity(user);
        user = this.userMapper.selectOne(wrapper);
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
        String day = df.format(new Date());
        String key = LOGIN_TIMES + day + ":" + name;
        if (user == null) {
            throw new ServiceException(UserErrorEnum.ADMIN_DISABLE_ERROR.getErrCode(), checkUserLoginTimes(key, UserErrorEnum.ADMIN_DISABLE_ERROR.getErrMsg()));
        }
        if (!password.equals(user.getUserPassword())) {//校验密码
            throw new ServiceException(UserErrorEnum.LOGIN_ERROR.getErrCode(), checkUserLoginTimes(key, UserErrorEnum.LOGIN_ERROR.getErrMsg()));
        }
        redisUtil.delete(key);
        return user;
    }


    public static boolean matches(String rawPassword, String encodedPassword) {
        try {
            if (StringUtils.hasText(rawPassword) && StringUtils.hasText(encodedPassword)) {
                final String newSecurityKey = Md5Util.md5(rawPassword);
                if (newSecurityKey.equals(encodedPassword)) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }

        } catch (final Exception e) {
            return false;
        }
    }

    /*
     * 记录用户登录次数，返回提示信息
     * @param key
     * @param msg
     * @return java.lang.String
     */
    public String checkUserLoginTimes(String key, String msg) {
        String val = redisUtil.get(key);
        if (val == null) {
            redisUtil.incrBy(key, 1L);
            redisUtil.expire(key, 1, TimeUnit.DAYS);
            return msg + ("，今日当前账号还可以登录" + (loginTimesLimit - 1) + "次！");
        }
        int loginTimes = Integer.parseInt(val);
        if (loginTimes >= loginTimesLimit) {
            Long expire = redisUtil.getExpire(key, TimeUnit.MINUTES);
            msg += "，当前账号已被锁定，请" + (expire + 1L) + "分钟后再次登录！";
        } else {
            redisUtil.incrBy(key, 1L);
            loginTimes = loginTimesLimit - loginTimes - 1;
            if (loginTimes == 0) {
                redisUtil.expire(key, loginTimesExpire, TimeUnit.MINUTES);
                msg += "，当前账号已被锁定,请" + loginTimesExpire + "分钟后再次登录！";
            } else {
                msg += "，今日当前账号还可以登录" + loginTimes + "次！";
            }
        }
        return msg;
    }
}
